""" 
A basic forum model with corresponding thread/post models.

Just about all logic required for smooth updates is in the save() methods. A
little extra logic is in views.py.
"""

from datetime import datetime

from django.db import models
from django.db.models import permalink
from django.contrib.auth.models import User


class Forum(models.Model):
    """
    Very basic outline for a Forum, or group of threads. The threads and posts
    fields are updated by the save() methods of their respective models and
    are used for display purposes.
    """
    
    title               = models.CharField(maxlength=50)
    slug                = models.SlugField(prepopulate_from=("title",))
    description         = models.TextField()
    threads             = models.IntegerField(default=0)
    posts               = models.IntegerField(default=0)
    forum_latest_post   = models.ForeignKey('Post', blank=True, null=True, related_name='forum_latest_post') # Must allow Null for delete
    
    class Admin:
        pass
    
    @permalink
    def get_absolute_url(self):
        return ('forum-forum', (), {'forum': self.slug})
    
    def __unicode__(self):
        return self.title
    

class Thread(models.Model):
    """
    A Thread belongs in a Forum, and is a collection of posts.
    
    Threads can be closed or stickied which alter their behaviour in the
    thread listings. Again, the posts & views fields are automatically updated
    with saving a post or viewing the thread.
    """
    
    forum               = models.ForeignKey(Forum)
    title               = models.CharField(maxlength=60)
    sticky              = models.BooleanField(blank=True, default=False)
    closed              = models.BooleanField(blank=True, default=False)
    posts               = models.IntegerField(default=0)
    views               = models.IntegerField(default=0)
    added               = models.DateTimeField('Date Created', editable=False)
    updated             = models.DateTimeField('Date Updated', editable=False)
    thread_latest_post  = models.ForeignKey('Post', blank=True, null=True, related_name='thread_latest_post')
    watching            = models.ManyToManyField(User, blank=True, null=True, verbose_name='Users Watching')
    watching_emailed    = models.ManyToManyField(User, blank=True, null=True, related_name='thread_emailed', verbose_name='Watching Users E-mailed', help_text='Watching users that have been sent an E-mail for the thread')
    
    class Meta:
        ordering = ('-sticky', '-thread_latest_post')
    
    class Admin:
        pass
    
    @permalink
    def get_absolute_url(self):
        return ('forum-thread', (), {'forum': self.forum.slug, 'thread': self.id})
    
    @permalink
    def get_watch_url(self):
        return ('forum-thread-watch', (), {'forum': self.forum.slug, 'thread': self.id})
    
    @permalink
    def get_unwatch_url(self):
        return ('forum-thread-unwatch', (), {'forum': self.forum.slug, 'thread': self.id})
    
    def __unicode__(self):
        return self.title
    
    def save(self):
        if not self.id:
            self.added = datetime.now()
            self.updated = self.added
        else:
            self.updated = datetime.now()
        if not self.id:
            f = Forum.objects.get(id=self.forum.id)
            f.threads += 1
            f.save()
        super(Thread, self).save()
    
    def delete(self):
        # Remove cached foreign keys to stop delete cascading
        if self.forum.forum_latest_post.id == self.thread_latest_post.id:
            self.forum.forum_latest_post = None
            self.forum.save()
        
        super(Thread, self).delete()
        
        # Fix forum cache
        if self.forum.forum_latest_post == None:
            last = Post.objects.order_by('-time').filter(thread__forum=self.forum)[:1]
            if len(last):
                self.forum.forum_latest_post = last[0]
                self.forum.save()
    
    def get_first_post(self):
        return self.post_set.all().order_by('time')[:1][0]
    
    def get_second_last_post(self):
        """
        Used when deleting the last post
        
        WARNING: Will error when there is only one post in thread.
        """
        return self.post_set.all().order_by('-time')[:2][1]
    
    def is_multipage(self):
        if self.post_set.count() > 20:
            return True
        return False
    
    def get_last_page_url(self):
        if self.is_multipage():
            return '%s?page=%s' % (self.get_absolute_url(), int(self.post_set.count() / 20) + 1)
        return self.get_absolute_url()
    
    def is_watching(self, user):
        if self.watching.filter(pk=user.id).count() > 0:
            return True
        return False


class Post(models.Model):
    """ 
    A Post is a User's input to a thread. Uber-basic - the save() method also
    updates models further up the heirarchy (Thread,Forum)
    """
    
    thread              = models.ForeignKey(Thread)
    author              = models.ForeignKey(User)
    body                = models.TextField(help_text="Uses MarkDown and bbCode for styling")
    time                = models.DateTimeField(editable=False)
    updated             = models.DateTimeField(editable=False)
    
    class Meta:
        ordering = ('time',)
    
    class Admin:
        pass
    
    def get_absolute_url(self):
        posts = self.thread.post_set.all()
        number = 0
        try:
            for post in posts:
                if post.id == self.id:
                    break
                number += 1
        except:
            pass
        page = int(number / 20) + 1
        if page > 1:
            page = '?page=%s' % page
        else:
            page = ''
        return '%s%s#post%s' % (self.thread.get_absolute_url(), page, self.id)
    
    @permalink
    def get_quote_url(self):
        return ('forum-post-quote', (), {'forum': self.thread.forum.slug, 'thread': self.thread.id, 'post': self.id})
    
    @permalink
    def get_edit_url(self):
        return ('forum-post-edit', (), {'forum': self.thread.forum.slug, 'thread': self.thread.id, 'post': self.id})
    
    @permalink
    def get_delete_url(self):
        return ('forum-post-delete', (), {'forum': self.thread.forum.slug, 'thread': self.thread.id, 'post': self.id})
    
    def __unicode__(self):
        return "%s" % self.id
    
    def save(self):
        new_post = False
        if not self.id:
            new_post = True
        
        if new_post:
            self.time = datetime.now()
            self.updated = self.time
        else:
            self.updated = datetime.now()
        
        super(Post, self).save()
        
        if new_post:
            t = Thread.objects.get(id=self.thread.id)
            t.thread_latest_post_id = self.id
            t.posts += 1
            t.save()
            
            f = Forum.objects.get(id=self.thread.forum.id)
            f.forum_latest_post_id = self.id
            f.posts += 1
            f.save()
    
    def delete(self):
        # Remove cached foreign keys to stop delete cascading
        if self.thread.thread_latest_post.id == self.id:
            self.thread.thread_latest_post = self.thread.get_second_last_post()
            self.thread.save()
        if self.thread.forum.forum_latest_post.id == self.id:
            self.thread.forum.forum_latest_post = None
            self.thread.forum.save()
        
        super(Post, self).delete()
        
        # Fix forum cache
        if self.thread.forum.forum_latest_post == None:
            last = Post.objects.order_by('-time').filter(thread__forum=self.thread.forum)[:1]
            if len(last):
                self.thread.forum.forum_latest_post = last[0]
                self.thread.forum.save()
    
